/*
 * $Id: iptc.c,v 1.61 2006-04-25 07:42:42 bacon Exp $
 */

#include <xp/lnx/iptc.h>
#include <xp/bas/memory.h>
#include <xp/bas/string.h>
#include <xp/bas/mstring.h>
#include <xp/bas/locale.h>
#include <xp/bas/endian.h>

struct ipt_entry* __make_entry (
	xp_iptc_t* iptc, const xp_char_t* target_name, 
	xp_bool_t inv_proto, int proto,
	xp_bool_t inv_src_ip, xp_uint32_t src_ip, xp_uint32_t src_mask,
	xp_bool_t inv_dst_ip, xp_uint32_t dst_ip, xp_uint32_t dst_mask,
	xp_bool_t inv_inifce, const xp_char_t* inifce,
	xp_bool_t inv_outifce, const xp_char_t* outifce,
	xp_bool_t inv_src_pt, xp_uint16_t src_pt_start, xp_uint16_t src_pt_end,
	xp_bool_t inv_dst_pt, xp_uint16_t dst_pt_start, xp_uint16_t dst_pt_end,
	int match_mac, const xp_byte_t src_mac[XP_ETHER_LEN],
	int match_rate, xp_uint32_t rate_avg, xp_uint32_t rate_burst,
	int match_mark, unsigned long mark, unsigned long mark_mask,
	int match_icmp, const xp_uint8_t icmp_info[3],
	xp_uint16_t redir_pt_start, xp_uint16_t redir_pt_end,
	unsigned long target_mark, int reject_type,
	xp_uint32_t snat_ip_start, xp_uint32_t snat_ip_end);

#define LOAD_IPTC_IF_NECESSARY(iptc) \
	do { if (!iptc->loaded && __load_iptc(iptc) == -1) return -1; } while(0)

static int __load_iptc (xp_iptc_t* iptc)
{
	iptc->handle = iptc_init (iptc->table);	
	if (iptc->handle == XP_NULL) return -1;
	iptc->loaded = xp_true;
	return 0;
}

xp_iptc_t* xp_iptc_open (xp_iptc_t* iptc, const xp_char_t* table_name)
{
	xp_size_t n;

	if (iptc == XP_NULL) {
		iptc = (xp_iptc_t*)xp_malloc (xp_sizeof(xp_iptc_t));
		if (iptc == XP_NULL) return XP_NULL;
		iptc->__dynamic = xp_true;
	}
	else iptc->__dynamic = xp_false;

#ifdef XP_CHAR_IS_MCHAR
	n = xp_strxcpy (iptc->table, xp_countof(iptc->table), table_name);
	if (n < xp_strlen(table_name)) return XP_NULL;
#else	
	n = xp_wcstomcs (table_name, iptc->table, xp_countof(iptc->table));
	if (n == (xp_size_t)-1 || n < xp_strlen(table_name)) return XP_NULL;
#endif

	iptc->loaded = xp_false;
	if (__load_iptc(iptc) == -1) {
		if (iptc->__dynamic) xp_free (iptc);
		return XP_NULL;
	}

	iptc->entry = XP_NULL;
	iptc->entry_capacity = 0;

	return iptc;
}

void xp_iptc_close (xp_iptc_t* iptc)
{
	if (iptc->loaded) {
		iptc_free (&iptc->handle);
		iptc->loaded = xp_false;
	}
	if (iptc->entry != XP_NULL) {
		xp_free (iptc->entry);
		iptc->entry = XP_NULL;
	}

	if (iptc->__dynamic) xp_free (iptc);
}

int xp_iptc_commit (xp_iptc_t* iptc)
{
	if (iptc->loaded &&
	    iptc_commit(&iptc->handle) == 0) return -1;
	iptc->loaded = xp_false;
	return 0;
}

void xp_iptc_abort (xp_iptc_t* iptc)
{
	if (iptc->loaded) {
		iptc_free (&iptc->handle);
		iptc->loaded = xp_false;
	}
}

int xp_iptc_reset (xp_iptc_t* iptc)
{
	if (iptc->loaded) {
		iptc_free (&iptc->handle);
		iptc->loaded = xp_false;
	}

	return __load_iptc(iptc);
}

void xp_iptc_set_changed (xp_iptc_t* iptc)
{
	iptc_set_changed (iptc->handle);
}

int xp_iptc_create_chain (xp_iptc_t* iptc, const xp_char_t* name)
{
	ipt_chainlabel chain;

#ifdef XP_CHAR_IS_MCHAR
	xp_strxcpy (chain, xp_countof(chain), name);
#else
	xp_size_t n;
	n = xp_wcstomcs (name, chain, xp_countof(chain));
	if (n == (xp_size_t)-1 || n < xp_strlen(name)) return -1;
#endif

	LOAD_IPTC_IF_NECESSARY (iptc);

	/* check if the chain exists */
	if (iptc_is_chain (chain, iptc->handle)) return -1;

	/* otherwise create the chain */
	if (iptc_create_chain (chain, &iptc->handle) == 0) return -1;
	return 0;
}

int xp_iptc_delete_chain (xp_iptc_t* iptc, const xp_char_t* name)
{
	ipt_chainlabel chain;

#ifdef XP_CHAR_IS_MCHAR
	xp_strxcpy (chain, xp_countof(chain), name);
#else
	xp_size_t n;
	n = xp_wcstomcs (name, chain, xp_countof(chain));
	if (n == (xp_size_t)-1 || n < xp_strlen(name)) return -1;
#endif

	LOAD_IPTC_IF_NECESSARY (iptc);

	if (!iptc_is_chain (chain, iptc->handle)) return -1;
	if (iptc_delete_chain (chain, &iptc->handle) == 0) return -1;
	return 0;
}

int xp_iptc_zero_chain (xp_iptc_t* iptc, const xp_char_t* name)
{
	ipt_chainlabel chain;

#ifdef XP_CHAR_IS_MCHAR
	xp_strxcpy (chain, xp_countof(chain), name);
#else
	xp_size_t n;
	n = xp_wcstomcs (name, chain, xp_countof(chain));
	if (n == (xp_size_t)-1 || n < xp_strlen(name)) return -1;
#endif

	LOAD_IPTC_IF_NECESSARY (iptc);

	if (iptc_zero_entries (chain, &iptc->handle) == 0) return -1;
	return 0;
}

int xp_iptc_flush_chain (xp_iptc_t* iptc, const xp_char_t* name)
{
	ipt_chainlabel chain;

#ifdef XP_CHAR_IS_MCHAR
	xp_strxcpy (chain, xp_countof(chain), name);
#else
	xp_size_t n;
	n = xp_wcstomcs (name, chain, xp_countof(chain));
	if (n == (xp_size_t)-1 || n < xp_strlen(name)) return -1;
#endif

	LOAD_IPTC_IF_NECESSARY (iptc);

	if (iptc_flush_entries (chain, &iptc->handle) == 0) return -1;
	return 0;
}

int xp_iptc_walk_chain (xp_iptc_t* iptc, 
	int (*func) (xp_iptc_t*, const xp_char_t*))
{
	int ret = 0;
	const xp_mchar_t *chain;
	xp_mchar_t *chains;

	xp_size_t i, chaincount = 0;

	LOAD_IPTC_IF_NECESSARY (iptc);

	chain = iptc_first_chain(&iptc->handle);
	while (chain != XP_NULL) {
		chaincount++;
		chain = iptc_next_chain(&iptc->handle);
	}

	chains = (xp_mchar_t*)xp_malloc(xp_sizeof(ipt_chainlabel) * chaincount);
	if (chains == XP_NULL) return -1;

	i = 0; chain = iptc_first_chain(&iptc->handle);
	while (chain != XP_NULL) {
		xp_mstrcpy(&chains[i*xp_sizeof(ipt_chainlabel)], chain);
		i++; chain = iptc_next_chain(&iptc->handle);
	}

	for (i = 0; i < chaincount; i++) {
		xp_mchar_t* c = &chains[i*xp_sizeof(ipt_chainlabel)];

		/* not allowed to delete builtin chains */
		if (iptc_builtin (c, iptc->handle) && 
		    func == xp_iptc_delete_chain) continue;

#ifdef XP_CHAR_IS_MCHAR
		if (func (iptc, c) == -1) {
			ret = -1;
			break;
		}
#else
		xp_wchar_t name[xp_sizeof(ipt_chainlabel)];
		xp_size_t n;
		n = xp_mcstowcs (c, name, xp_countof(name));
		if (n == (xp_size_t)-1 || n < xp_mstrlen(c)) {
			ret = -1;
			break;
		}

		if (func(iptc, name) == -1) {
			ret = -1;
			break;
		}
#endif
	}

	xp_free (chains);
	return ret;
}

const xp_iptc_entry_t* xp_iptc_first_rule (
	xp_iptc_t* iptc, const xp_char_t* name,
	xp_iptc_entry_t const** rule_end)
{
	ipt_chainlabel chain;
	const xp_iptc_entry_t* tmp;

#ifdef XP_CHAR_IS_MCHAR
	xp_strxcpy (chain, xp_countof(chain), name);
#else
	xp_size_t n;
	n = xp_wcstomcs (name, chain, xp_countof(chain));
	if (n == (xp_size_t)-1 || n < xp_strlen(name)) return XP_NULL;
#endif

	tmp = iptc_first_rule (chain, &iptc->handle);

	/* dirty hack to work around libiptc bugs */
	*rule_end = iptc_get_cache_rule_end (iptc->handle);
	return tmp;
}

const xp_iptc_entry_t* xp_iptc_next_rule (
	xp_iptc_t* iptc, const xp_iptc_entry_t* previous,
	xp_iptc_entry_t const** rule_end)
{
	const xp_iptc_entry_t* x;
	/* dirty hack to work around libiptc bugs */
	iptc_set_cache_rule_end (*rule_end, iptc->handle);

	x = iptc_next_rule (previous, &iptc->handle);
	*rule_end = iptc_get_cache_rule_end (iptc->handle);
	return x;
}

int xp_iptc_insert_entry (
	xp_iptc_t* iptc,
	const xp_char_t* chain_name, xp_size_t pos,
	const xp_char_t* target_name, 
	xp_bool_t inv_proto, int proto,
	xp_bool_t inv_src_ip, xp_uint32_t src_ip, xp_uint32_t src_mask,
	xp_bool_t inv_dst_ip, xp_uint32_t dst_ip, xp_uint32_t dst_mask,
	xp_bool_t inv_inifce, const xp_char_t* inifce,
	xp_bool_t inv_outifce, const xp_char_t* outifce,
	xp_bool_t inv_src_pt, xp_uint16_t src_pt_start, xp_uint16_t src_pt_end,
	xp_bool_t inv_dst_pt, xp_uint16_t dst_pt_start, xp_uint16_t dst_pt_end,
	int match_mac, const xp_byte_t src_mac[XP_ETHER_LEN],
	int match_rate, xp_uint32_t rate_avg, xp_uint32_t rate_burst,
	int match_mark, unsigned long mark, unsigned long mark_mask,
	int match_icmp, const xp_uint8_t icmp_info[3],
	xp_uint16_t redir_pt_start, xp_uint16_t redir_pt_end,
	unsigned long target_mark, int reject_type,
	xp_uint32_t snat_ip_start, xp_uint32_t snat_ip_end)
{
	xp_size_t n;
	ipt_chainlabel chain;
	struct ipt_entry* entry;

#ifdef XP_CHAR_IS_MCHAR
	n = xp_strxcpy (chain, xp_countof(chain), chain_name);
	if (n < xp_strlen(chain_name)) return -1;
#else
	n = xp_wcstomcs (chain_name, chain, xp_countof(chain));
	if (n == (xp_size_t)-1 || n < xp_strlen(chain_name)) return -1;
#endif

	LOAD_IPTC_IF_NECESSARY (iptc);

	entry = __make_entry (
		iptc, target_name, 
		inv_proto, proto,
		inv_src_ip, src_ip, src_mask, 
		inv_dst_ip, dst_ip, dst_mask, 
		inv_inifce, inifce, inv_outifce, outifce,
		inv_src_pt, src_pt_start, src_pt_end, 
		inv_dst_pt, dst_pt_start, dst_pt_end,
		match_mac, src_mac, 
		match_rate, rate_avg, rate_burst,
		match_mark, mark, mark_mask,
		match_icmp, icmp_info,
		redir_pt_start, redir_pt_end,
		target_mark, reject_type,
		snat_ip_start, snat_ip_end);
	if (entry == XP_NULL) return -1;

	if (iptc_insert_entry (chain, entry, pos, &iptc->handle) == 0) {
		/* xp_free (entry); */
		return -1;
	}

	/* xp_free (entry); */
	return 0;
}


int xp_iptc_append_entry (
	xp_iptc_t* iptc, 
	const xp_char_t* chain_name, 
	const xp_char_t* target_name, 
	xp_bool_t inv_proto, int proto,
	xp_bool_t inv_src_ip, xp_uint32_t src_ip, xp_uint32_t src_mask,
	xp_bool_t inv_dst_ip, xp_uint32_t dst_ip, xp_uint32_t dst_mask,
	xp_bool_t inv_inifce, const xp_char_t* inifce,
	xp_bool_t inv_outifce, const xp_char_t* outifce,
	xp_bool_t inv_src_pt, xp_uint16_t src_pt_start, xp_uint16_t src_pt_end,
	xp_bool_t inv_dst_pt, xp_uint16_t dst_pt_start, xp_uint16_t dst_pt_end,
	int match_mac, const xp_byte_t src_mac[XP_ETHER_LEN],
	int match_rate, xp_uint32_t rate_avg, xp_uint32_t rate_burst,
	int match_mark, unsigned long mark, unsigned long mark_mask,
	int match_icmp, const xp_uint8_t icmp_info[3],
	xp_uint16_t redir_pt_start, xp_uint16_t redir_pt_end,
	unsigned long target_mark, int reject_type,
	xp_uint32_t snat_ip_start, xp_uint32_t snat_ip_end)
{
	xp_size_t n;
	ipt_chainlabel chain;
	struct ipt_entry* entry;

#ifdef XP_CHAR_IS_MCHAR
	n = xp_strxcpy (chain, xp_countof(chain), chain_name);
	if (n < xp_strlen(chain_name)) return -1;
#else
	n = xp_wcstomcs (chain_name, chain, xp_countof(chain));
	if (n == (xp_size_t)-1 || n < xp_strlen(chain_name)) return -1;
#endif

	LOAD_IPTC_IF_NECESSARY (iptc);

	entry = __make_entry (
		iptc, target_name, 
		inv_proto, proto,
		inv_src_ip, src_ip, src_mask, 
		inv_dst_ip, dst_ip, dst_mask, 
		inv_inifce, inifce, inv_outifce, outifce,
		inv_src_pt, src_pt_start, src_pt_end, 
		inv_dst_pt, dst_pt_start, dst_pt_end,
		match_mac, src_mac, 
		match_rate, rate_avg, rate_burst,
		match_mark, mark, mark_mask,
		match_icmp, icmp_info,
		redir_pt_start, redir_pt_end,
		target_mark, reject_type,
		snat_ip_start, snat_ip_end);
	if (entry == XP_NULL) return -1;

	if (iptc_append_entry (chain, entry, &iptc->handle) == 0) {
		/* xp_free (entry); */
		return -1;
	}

	/* xp_free (entry); */
	return 0;
}

int xp_iptc_delete_entry (
	xp_iptc_t* iptc, 
	const xp_char_t* chain_name, 
	const xp_char_t* target_name, 
	xp_bool_t inv_proto, int proto,
	xp_bool_t inv_src_ip, xp_uint32_t src_ip, xp_uint32_t src_mask,
	xp_bool_t inv_dst_ip, xp_uint32_t dst_ip, xp_uint32_t dst_mask,
	xp_bool_t inv_inifce, const xp_char_t* inifce,
	xp_bool_t inv_outifce, const xp_char_t* outifce,
	xp_bool_t inv_src_pt, xp_uint16_t src_pt_start, xp_uint16_t src_pt_end,
	xp_bool_t inv_dst_pt, xp_uint16_t dst_pt_start, xp_uint16_t dst_pt_end,
	int match_mac, const xp_byte_t src_mac[XP_ETHER_LEN],
	int match_rate, xp_uint32_t rate_avg, xp_uint32_t rate_burst,
	int match_mark, unsigned long mark, unsigned long mark_mask,
	int match_icmp, const xp_uint8_t icmp_info[3],
	xp_uint16_t redir_pt_start, xp_uint16_t redir_pt_end,
	unsigned long target_mark, int reject_type,
	xp_uint32_t snat_ip_start, xp_uint32_t snat_ip_end)
{
	xp_size_t n;
	ipt_chainlabel chain;
	struct ipt_entry* entry;
	xp_byte_t* match_mask;

#ifdef XP_CHAR_IS_MCHAR
	n = xp_strxcpy (chain, xp_countof(chain), chain_name);
	if (n < xp_strlen(chain_name)) return -1;
#else
	n = xp_wcstomcs (chain_name, chain, xp_countof(chain));
	if (n == (xp_size_t)-1 || n < xp_strlen(chain_name)) return -1;
#endif

	LOAD_IPTC_IF_NECESSARY (iptc);

	entry = __make_entry (
		iptc, target_name,
		inv_proto, proto,
		inv_src_ip, src_ip, src_mask, 
		inv_dst_ip, dst_ip, dst_mask, 
		inv_inifce, inifce, inv_outifce, outifce,
		inv_src_pt, src_pt_start, src_pt_end, 
		inv_dst_pt, dst_pt_start, dst_pt_end,
		match_mac, src_mac, 
		match_rate, rate_avg, rate_burst,
		match_mark, mark, mark_mask,
		match_icmp, icmp_info,
		redir_pt_start, redir_pt_end,
		target_mark, reject_type,
		snat_ip_start, snat_ip_end);
	if (entry == XP_NULL) return -1;

	match_mask = (unsigned char *)xp_malloc (entry->next_offset);
	if (match_mask == XP_NULL) {
		xp_free (entry);
		return -1;
	}
	xp_memset (match_mask, 0xFF, entry->next_offset);

	if (iptc_delete_entry (chain, entry, match_mask, &iptc->handle) == 0) {
		xp_free (match_mask);
		/* xp_free (entry); */
		return -1;
	}

	xp_free (match_mask);
	/* xp_free (entry); */
	return 0;
}

int xp_iptc_delete_entry_at (
	xp_iptc_t* iptc, const xp_char_t* chain_name, xp_size_t pos)
{
	xp_size_t n;
	ipt_chainlabel chain;

#ifdef XP_CHAR_IS_MCHAR
	n = xp_strxcpy (chain, xp_countof(chain), chain_name);
	if (n < xp_strlen(chain_name)) return -1;
#else
	n = xp_wcstomcs (chain_name, chain, xp_countof(chain));
	if (n == (xp_size_t)-1 || n < xp_strlen(chain_name)) return -1;
#endif

	if (iptc_delete_num_entry (chain, pos, &iptc->handle) == 0) {
		return -1;
	}

	return 0;
}

struct ipt_entry* __make_entry (
	xp_iptc_t* iptc, const xp_char_t* target_name, 
	/* ipt_ip */
	xp_bool_t inv_proto, int proto,
	xp_bool_t inv_src_ip, xp_uint32_t src_ip, xp_uint32_t src_mask,
	xp_bool_t inv_dst_ip, xp_uint32_t dst_ip, xp_uint32_t dst_mask,
	xp_bool_t inv_inifce, const xp_char_t* inifce,
	xp_bool_t inv_outifce, const xp_char_t* outifce,
	/* matches */
	xp_bool_t inv_src_pt, xp_uint16_t src_pt_start, xp_uint16_t src_pt_end,
	xp_bool_t inv_dst_pt, xp_uint16_t dst_pt_start, xp_uint16_t dst_pt_end,
	int match_mac, const xp_byte_t src_mac[XP_ETHER_LEN],
	int match_rate, xp_uint32_t rate_avg, xp_uint32_t rate_burst,
	int match_mark, unsigned long mark, unsigned long mark_mask,
	int match_icmp, const xp_uint8_t icmp_info[3],
	/* target options */
	xp_uint16_t redir_pt_start, xp_uint16_t redir_pt_end,
	unsigned long target_mark, int reject_type,
	xp_uint32_t snat_ip_start, xp_uint32_t snat_ip_end)
{
	struct ipt_entry* entry = XP_NULL;
	struct ipt_entry_match* match = XP_NULL, * match_base = XP_NULL;
	struct ipt_entry_target* target, * target_base = XP_NULL;
	xp_size_t n, size, match_base_size, target_base_size;
	xp_size_t match_offset[6], match_size[6];
	xp_size_t target_offset[1], target_size[1];
	enum {
		TARGET_NORMAL,
		TARGET_REDIRECT,
		TARGET_MARK,
		TARGET_REJECT,
		TARGET_SNAT,
		TARGET_DNAT
	} target_type;
#ifdef XP_CHAR_IS_MCHAR
	const xp_char_t* dev_in = XP_NULL;
	const xp_char_t* dev_out = XP_NULL;
#else
	xp_mchar_t dev_in[IFNAMSIZ] = "";
	xp_mchar_t dev_out[IFNAMSIZ] = "";
#endif

#ifdef XP_CHAR_IS_MCHAR
	if (inifce != XP_NULL) {
		dev_in = inifce;
		if (xp_strlen(dev_in) >= IFNAMSIZ) return XP_NULL;
	}
	if (outifce != XP_NULL) {
		dev_out = outifce;
		if (xp_strlen(dev_out) >= IFNAMSIZ) return XP_NULL;
	}
#else
	if (inifce != XP_NULL) {
		n = xp_wcstomcs (inifce, dev_in, xp_countof(dev_in));
		if (n == (xp_size_t)-1 || n < xp_strlen(inifce)) return XP_NULL;
	}
	if (outifce != XP_NULL) {
		n = xp_wcstomcs (outifce, dev_out, xp_countof(dev_out));
		if (n == (xp_size_t)-1 || n < xp_strlen(outifce)) return XP_NULL;
	}
#endif

	target_type = 
		(xp_strcmp(target_name,XP_TEXT("REDIRECT")) == 0)? TARGET_REDIRECT:
		(xp_strcmp(target_name,XP_TEXT("MARK")) == 0)? TARGET_MARK:
		(xp_strcmp(target_name,XP_TEXT("REJECT")) == 0)? TARGET_REJECT:
		(xp_strcmp(target_name,XP_TEXT("SNAT")) == 0)? TARGET_SNAT:
		(xp_strcmp(target_name,XP_TEXT("DNAT")) == 0)? TARGET_DNAT: TARGET_NORMAL;
		
	/* decide the match size */
	match_base_size = 0;
	if (proto == IPPROTO_TCP && 
	    (!(src_pt_start == 0x0000 && src_pt_end == 0xFFFF) ||
	     !(dst_pt_start == 0x0000 && dst_pt_end == 0xFFFF))) {
		match_offset[0] = match_base_size;
		match_size[0] = 
			xp_sizeof(struct ipt_entry_match) + 
			IPT_ALIGN(xp_sizeof(struct ipt_tcp));
		match_base_size += match_size[0];
	}
	if (proto == IPPROTO_UDP &&
	    (!(src_pt_start == 0x0000 && src_pt_end == 0xFFFF) ||
	     !(dst_pt_start == 0x0000 && dst_pt_end == 0xFFFF))) {
		match_offset[1] = match_base_size;
		match_size[1] = 
			xp_sizeof(struct ipt_entry_match) +
			IPT_ALIGN(xp_sizeof(struct ipt_udp));
		match_base_size += match_size[1];
	}
	if (match_mac != 0) {
		match_offset[2] = match_base_size;
		match_size[2] = 
			xp_sizeof(struct ipt_entry_match) +
			IPT_ALIGN(xp_sizeof(struct ipt_mac_info));
		match_base_size += match_size[2];
	}
	if (match_rate != 0) {
		match_offset[3] = match_base_size;
		match_size[3] = 
			xp_sizeof(struct ipt_entry_match) +
			IPT_ALIGN(xp_sizeof(struct ipt_rateinfo));
		match_base_size += match_size[3];
	}
	if (match_mark != 0) {
		match_offset[4] = match_base_size;
		match_size[4] = 
			xp_sizeof(struct ipt_entry_match) +
			IPT_ALIGN(xp_sizeof(struct ipt_mark_info));
		match_base_size += match_size[4];
	}
	if (match_icmp != 0) {
		match_offset[5] = match_base_size;
		match_size[5] = 
			xp_sizeof(struct ipt_entry_match) +
			IPT_ALIGN(xp_sizeof(struct ipt_icmp));
		match_base_size += match_size[5];
	}

	/* decide the target size */
	target_base_size = 0;
	if (target_type == TARGET_REDIRECT) {
		target_offset[0] = target_base_size;
		target_size[0] = 
			xp_sizeof(struct ipt_entry_target) +
			IPT_ALIGN(xp_sizeof(struct ip_nat_multi_range));
		target_base_size += target_size[0];
	}
	else if (target_type == TARGET_MARK) {
		target_offset[0] = target_base_size;
		target_size[0] = 
			xp_sizeof(struct ipt_entry_target) +
			IPT_ALIGN(xp_sizeof(struct ipt_mark_target_info));
		target_base_size += target_size[0];
	}
	else if (target_type == TARGET_REJECT) {
		target_offset[0] = target_base_size;
		target_size[0] = 
			xp_sizeof(struct ipt_entry_target) +
			IPT_ALIGN(xp_sizeof(struct ipt_reject_info));
		target_base_size += target_size[0];
	}
	else if (target_type == TARGET_SNAT) {
		target_offset[0] = target_base_size;
		target_size[0] = 
			xp_sizeof(struct ipt_entry_target) +
			IPT_ALIGN(xp_sizeof(struct ip_nat_multi_range));
		target_base_size += target_size[0];
	}
	else if (target_type == TARGET_DNAT) {
		target_offset[0] = target_base_size;
		target_size[0] = 
			xp_sizeof(struct ipt_entry_target) +
			IPT_ALIGN(xp_sizeof(struct ip_nat_multi_range));
		target_base_size += target_size[0];
	}
#if 0
	else if (target_type == TARGET_MASQUERADE) {
		target_offset[0] = target_base_size;
		target_size[0] = 
			xp_sizeof(struct ipt_entry_target) +
			IPT_ALIGN(xp_sizeof(struct ip_nat_multi_range));
		target_base_size += target_size[0];
	}
#endif
	else {
		target_offset[0] = target_base_size;
		target_size[0] = 
			xp_sizeof(struct ipt_entry_target) +
			IPT_ALIGN(xp_sizeof(int));
		target_base_size += target_size[0];
	}

	size = xp_sizeof(struct ipt_entry) + match_base_size + target_base_size;

	#define XXXX 10240
	{
		xp_size_t cap = (size / XXXX + 1) * XXXX;
		if (cap > iptc->entry_capacity) {
			if (iptc->entry != XP_NULL) xp_free (iptc->entry);
			iptc->entry = (struct ipt_entry*)xp_malloc (cap);
			if (iptc->entry == XP_NULL) iptc->entry_capacity = 0;
			else iptc->entry_capacity = cap;
		}
		entry = iptc->entry;
	}
	/* entry = (struct ipt_entry*) xp_malloc (size); */
	if (entry == XP_NULL) return XP_NULL;

	/* build main entry */
	xp_memset (entry, 0, size);
	match_base = (struct ipt_entry_match*)
		((xp_byte_t*)entry + xp_sizeof(struct ipt_entry));
	target_base = (struct ipt_entry_target*)
		((xp_byte_t*)entry + xp_sizeof(struct ipt_entry) + match_base_size);

	entry->target_offset = xp_sizeof(struct ipt_entry) + match_base_size;
	entry->next_offset = size;

	entry->ip.proto = proto;
	entry->ip.src.s_addr = src_ip;
	entry->ip.dst.s_addr = dst_ip;
	entry->ip.smsk.s_addr = src_mask;
	entry->ip.dmsk.s_addr = dst_mask; 
	if (inifce != XP_NULL) {
		n = xp_mstrxcpy (entry->ip.iniface,
			xp_countof(entry->ip.iniface), dev_in);
		/* include the null-terminator in match */
		if (n > 0) xp_memset (entry->ip.iniface_mask, 0xFF, n + 1);
	}
	if (outifce != XP_NULL) {
		n = xp_mstrxcpy (entry->ip.outiface,
			xp_countof(entry->ip.outiface), dev_out);
		/* include the null-terminator in match */
		if (n > 0) xp_memset (entry->ip.outiface_mask, 0xFF, n + 1);
	}

	if (inv_proto) entry->ip.invflags |= IPT_INV_PROTO;
	if (inv_src_ip) entry->ip.invflags |= IPT_INV_SRCIP;
	if (inv_dst_ip) entry->ip.invflags |= IPT_INV_DSTIP;
	if (inv_inifce) entry->ip.invflags |= IPT_INV_VIA_IN;
	if (inv_outifce) entry->ip.invflags |= IPT_INV_VIA_OUT;

	/* build matches */
	if (proto == IPPROTO_TCP &&
	    (!(src_pt_start == 0x0000 && src_pt_end == 0xFFFF) ||
	     !(dst_pt_start == 0x0000 && dst_pt_end == 0xFFFF))) {
		struct ipt_tcp* data;
		match = (struct ipt_entry_match*)
			((xp_byte_t*)match_base + match_offset[0]);
		data = (struct ipt_tcp*)match->data;
	
		match->u.match_size = match_size[0];
		xp_mstrxcpy (match->u.user.name, 
			xp_countof(match->u.user.name), "tcp");
		data->spts[0] = src_pt_start;
		data->spts[1] = src_pt_end;
		data->dpts[0] = dst_pt_start;
		data->dpts[1] = dst_pt_end;

		if (inv_src_pt) data->invflags |= IPT_TCP_INV_SRCPT;
		if (inv_dst_pt) data->invflags |= IPT_TCP_INV_DSTPT;
	}

	if (proto == IPPROTO_UDP &&
	    (!(src_pt_start == 0x0000 && src_pt_end == 0xFFFF) ||
	     !(dst_pt_start == 0x0000 && dst_pt_end == 0xFFFF))) {
		struct ipt_udp* data;
		match = (struct ipt_entry_match*)
			((xp_byte_t*)match_base + match_offset[1]);
		data = (struct ipt_udp*)match->data;
	
		match->u.match_size = match_size[1];
		xp_mstrxcpy (match->u.user.name, 
			xp_countof(match->u.user.name), "udp");
		data->spts[0] = src_pt_start;
		data->spts[1] = src_pt_end;
		data->dpts[0] = dst_pt_start;
		data->dpts[1] = dst_pt_end;

		if (inv_src_pt) data->invflags |= IPT_TCP_INV_SRCPT;
		if (inv_dst_pt) data->invflags |= IPT_TCP_INV_DSTPT;
	}

	if (match_mac != 0) {
		struct ipt_mac_info* data;
		match = (struct ipt_entry_match*)
			((xp_byte_t*)match_base + match_offset[2]);
		data = (struct ipt_mac_info*)match->data;

		match->u.match_size = match_size[2];
		xp_mstrxcpy (match->u.user.name, 
			xp_countof(match->u.user.name), "mac");
		xp_memcpy (data->srcaddr, src_mac, xp_sizeof(data->srcaddr));
		data->invert = (match_mac > 0)? 0: 1;
	}
	if (match_rate != 0) {
		struct ipt_rateinfo* data;
		match = (struct ipt_entry_match*)
			((xp_byte_t*)match_base + match_offset[3]);
		data = (struct ipt_rateinfo*)match->data;

		/* 
		 * 1/10,000 sec period => max of 10,000/sec.  
		 * Min rate is then 429490 seconds, or one every 59 hours. 
		 */

		match->u.match_size = match_size[3];
		xp_mstrxcpy (match->u.user.name, 
			xp_countof(match->u.user.name), "limit");

		/* Average secs between packets * scale */
		data->avg = rate_avg;
		/* Period multiplier for upper limit. */
		data->burst = rate_burst;
	}
	if (match_mark != 0) {
		struct ipt_mark_info* data;
		match = (struct ipt_entry_match*)
			((xp_byte_t*)match_base + match_offset[4]);
		data = (struct ipt_mark_info*)match->data;

		match->u.match_size = match_size[4];
		xp_mstrxcpy (match->u.user.name, 
			xp_countof(match->u.user.name), "mark");

		data->mark = mark;
		data->mask = mark_mask;
		data->invert = (match_mark > 0)? 0: 1;
	}
	if (proto == IPPROTO_ICMP && match_icmp != 0) {
		struct ipt_icmp* data;
		match = (struct ipt_entry_match*)
			((xp_byte_t*)match_base + match_offset[5]);
		data = (struct ipt_icmp*)match->data;

		match->u.match_size = match_size[5];
		xp_mstrxcpy (match->u.user.name, 
			xp_countof(match->u.user.name), "icmp");

		data->type = icmp_info[0];
		data->code[0] = icmp_info[1];
		data->code[1] = icmp_info[2];
		data->invflags = (match_icmp > 0)? 0: 1;
	}

	/* build target */
	target = (struct ipt_entry_target*)
		((xp_byte_t*)target_base + target_offset[0]);
	target->u.target_size = target_size[0];
#ifdef XP_CHAR_IS_MCHAR
	xp_strxcpy (target->u.user.name, 
		xp_countof(target->u.user.name), target_name);
#else
	xp_wcstomcs (target_name, 
		target->u.user.name, xp_countof(target->u.user.name));
#endif
	if (target_type == TARGET_REDIRECT) {
		struct ip_nat_multi_range* data;
		data = (struct ip_nat_multi_range*)target->data;

		data->rangesize = 1;
		data->range[0].flags = IP_NAT_RANGE_PROTO_SPECIFIED;

		// TODO: more protocol check...
		if (proto == IPPROTO_TCP) {
			data->range[0].min.tcp.port = xp_hton16(redir_pt_start);
			data->range[0].max.tcp.port = xp_hton16(redir_pt_end);
		}
		else if (proto == IPPROTO_UDP) {
			data->range[0].min.udp.port = xp_hton16(redir_pt_start);
			data->range[0].max.udp.port = xp_hton16(redir_pt_end);
		}
	}	
	else if (target_type == TARGET_MARK) {
		struct ipt_mark_target_info* data;
		data = (struct ipt_mark_target_info*)target->data;
		data->mark = target_mark;
	}
	else if (target_type == TARGET_REJECT) {
		struct ipt_reject_info* data;
		data = (struct ipt_reject_info*)target->data;
		data->with = reject_type;
	}
	else if (target_type == TARGET_SNAT) {
		struct ip_nat_multi_range* data;
		data = (struct ip_nat_multi_range*)target->data;

		data->rangesize = 1;
		data->range[0].flags = IP_NAT_RANGE_MAP_IPS;
		data->range[0].min_ip = snat_ip_start;
		data->range[0].max_ip = snat_ip_end;
	}
	else if (target_type == TARGET_DNAT) {
		struct ip_nat_multi_range* data;
		data = (struct ip_nat_multi_range*)target->data;

		data->rangesize = 1;
		data->range[0].flags = IP_NAT_RANGE_MAP_IPS;
		data->range[0].min_ip = snat_ip_start;
		data->range[0].max_ip = snat_ip_end;

		if (redir_pt_start > 0 && redir_pt_end > 0) {
			if (proto == IPPROTO_TCP) {
				data->range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
				data->range[0].min.tcp.port = xp_hton16(redir_pt_start);
				data->range[0].max.tcp.port = xp_hton16(redir_pt_end);
			}
			else if (proto == IPPROTO_UDP) {
				data->range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
				data->range[0].min.udp.port = xp_hton16(redir_pt_start);
				data->range[0].max.udp.port = xp_hton16(redir_pt_end);
			}
		}
	}
#if 0
	else if (target_type == TARGET_MASQUERADE) {
		struct ip_nat_multi_range* data;
		data = (struct ip_nat_multi_range*)target->data;
		/* TODO */
	}
#endif

	return entry;
}


#if 0
struct ipt_entry* __make_entry (
	xp_iptc_t* iptc, 
	const xp_iptc_ipt_t* ipt,
	const xp_iptc_match_t** matches,
	const xp_iptc_target_t* target)
{
	const xp_iptc_match_t** mp;

	struct ipt_entry* entry = XP_NULL;
	struct ipt_entry_match* match = XP_NULL, * match_base = XP_NULL;
	struct ipt_entry_target* target, * target_base = XP_NULL;
	xp_size_t n, size, match_base_size, target_base_size;
	xp_size_t match_offset[5], match_size[5];
	xp_size_t target_offset[1], target_size[1];
	enum {
		TARGET_NORMAL,
		TARGET_REDIRECT,
		TARGET_MARK,
		TARGET_REJECT,
		TARGET_SNAT,
		TARGET_DNAT
	} target_type;
#ifdef XP_CHAR_IS_MCHAR
	const xp_char_t* dev_in = XP_NULL;
	const xp_char_t* dev_out = XP_NULL;
#else
	xp_mchar_t dev_in[IFNAMSIZ] = "";
	xp_mchar_t dev_out[IFNAMSIZ] = "";
#endif

#ifdef XP_CHAR_IS_MCHAR
	if (ipt->ifce.in != XP_NULL) {
		dev_in = ipt->ifce.in;
		if (xp_strlen(dev_in) >= IFNAMSIZ) return XP_NULL;
	}
	if (ipt->ifce.out != XP_NULL) {
		dev_out = ipt->ifce.out;
		if (xp_strlen(dev_out) >= IFNAMSIZ) return XP_NULL;
	}
#else
	if (ipt->ifce.in != XP_NULL) {
		n = xp_wcstomcs (ipt->ifce.in, dev_in, xp_countof(dev_in));
		if (n == (xp_size_t)-1 || 
		    n < xp_strlen(ipt->ifce.in)) return XP_NULL;
	}
	if (ipt->ifce.out != XP_NULL) {
		n = xp_wcstomcs (ipt->ifce.out, dev_out, xp_countof(dev_out));
		if (n == (xp_size_t)-1 || 
		    n < xp_strlen(ipt->ifce.out)) return XP_NULL;
	}
#endif

#if 0
	target_type = 
		(xp_strcmp(target_name,XP_TEXT("REDIRECT")) == 0)? TARGET_REDIRECT:
		(xp_strcmp(target_name,XP_TEXT("MARK")) == 0)? TARGET_MARK:
		(xp_strcmp(target_name,XP_TEXT("REJECT")) == 0)? TARGET_REJECT:
		(xp_strcmp(target_name,XP_TEXT("SNAT")) == 0)? TARGET_SNAT:
		(xp_strcmp(target_name,XP_TEXT("DNAT")) == 0)? TARGET_DNAT: TARGET_NORMAL;
#endif
		
	/* decide the match size */
	match_base_size = 0;

	for (mp = matches; *mp != XP_NULL; mp++) {
		xp_size_t mc = mp - matches;

		match_offset[mc] = match_base_size;

		if (mp->id == XP_IPTC_MATCH_TCP) {
			if (ipt->proto == IPPROTO_TCP && 

			match_size[mc] = 
				xp_sizeof(struct ipt_entry_match) + 
				IPT_ALIGN(xp_sizeof(struct ipt_tcp));
		}
		else if (mp->id == XP_IPTC_MATCH_UDP) {
			match_size[mc] = 
				xp_sizeof(struct ipt_entry_match) + 
				IPT_ALIGN(xp_sizeof(struct ipt_udp));
		}
		else if (mp->id == XP_IPTC_MATCH_MAC) {
			match_size[mc] = 
				xp_sizeof(struct ipt_entry_match) +
				IPT_ALIGN(xp_sizeof(struct ipt_mac_info));
		}
		else if (mp->id == XP_IPTC_MATCH_RATE) {
			match_size[mc] = 
				xp_sizeof(struct ipt_entry_match) +
				IPT_ALIGN(xp_sizeof(struct ipt_rateinfo));
		}
		else if (mp->id == XP_IPTC_MATCH_MARK) {
			match_size[mc] = 
				xp_sizeof(struct ipt_entry_match) +
				IPT_ALIGN(xp_sizeof(struct ipt_mark_info));
		}
		else {
			/* unknown match type */
			return XP_NULL;
		}

		match_base_size += match_size[mc];
	}

#ifdef 0
	if (ipt->proto == IPPROTO_TCP && 
	    (!(src_pt_start == 0x0000 && src_pt_end == 0xFFFF) ||
	     !(dst_pt_start == 0x0000 && dst_pt_end == 0xFFFF))) {
		match_offset[0] = match_base_size;
		match_size[0] = 
			xp_sizeof(struct ipt_entry_match) + 
			IPT_ALIGN(xp_sizeof(struct ipt_tcp));
		match_base_size += match_size[0];
	}
	if (ipt->proto == IPPROTO_UDP &&
	    (!(src_pt_start == 0x0000 && src_pt_end == 0xFFFF) ||
	     !(dst_pt_start == 0x0000 && dst_pt_end == 0xFFFF))) {
		match_offset[1] = match_base_size;
		match_size[1] = 
			xp_sizeof(struct ipt_entry_match) +
			IPT_ALIGN(xp_sizeof(struct ipt_udp));
		match_base_size += match_size[1];
	}
	if (match_mac != 0) {
		match_offset[2] = match_base_size;
		match_size[2] = 
			xp_sizeof(struct ipt_entry_match) +
			IPT_ALIGN(xp_sizeof(struct ipt_mac_info));
		match_base_size += match_size[2];
	}
	if (match_rate != 0) {
		match_offset[3] = match_base_size;
		match_size[3] = 
			xp_sizeof(struct ipt_entry_match) +
			IPT_ALIGN(xp_sizeof(struct ipt_rateinfo));
		match_base_size += match_size[3];
	}
	if (match_mark != 0) {
		match_offset[4] = match_base_size;
		match_size[4] = 
			xp_sizeof(struct ipt_entry_match) +
			IPT_ALIGN(xp_sizeof(struct ipt_mark_info));
		match_base_size += match_size[4];
	}
#endif

	/* decide the target size */
	target_base_size = 0;
	if (target_type == TARGET_REDIRECT) {
		target_offset[0] = target_base_size;
		target_size[0] = 
			xp_sizeof(struct ipt_entry_target) +
			IPT_ALIGN(xp_sizeof(struct ip_nat_multi_range));
		target_base_size += target_size[0];
	}
	else if (target_type == TARGET_MARK) {
		target_offset[0] = target_base_size;
		target_size[0] = 
			xp_sizeof(struct ipt_entry_target) +
			IPT_ALIGN(xp_sizeof(struct ipt_mark_target_info));
		target_base_size += target_size[0];
	}
	else if (target_type == TARGET_REJECT) {
		target_offset[0] = target_base_size;
		target_size[0] = 
			xp_sizeof(struct ipt_entry_target) +
			IPT_ALIGN(xp_sizeof(struct ipt_reject_info));
		target_base_size += target_size[0];
	}
	else if (target_type == TARGET_SNAT) {
		target_offset[0] = target_base_size;
		target_size[0] = 
			xp_sizeof(struct ipt_entry_target) +
			IPT_ALIGN(xp_sizeof(struct ip_nat_multi_range));
		target_base_size += target_size[0];
	}
	else if (target_type == TARGET_DNAT) {
		target_offset[0] = target_base_size;
		target_size[0] = 
			xp_sizeof(struct ipt_entry_target) +
			IPT_ALIGN(xp_sizeof(struct ip_nat_multi_range));
		target_base_size += target_size[0];
	}
#if 0
	else if (target_type == TARGET_MASQUERADE) {
		target_offset[0] = target_base_size;
		target_size[0] = 
			xp_sizeof(struct ipt_entry_target) +
			IPT_ALIGN(xp_sizeof(struct ip_nat_multi_range));
		target_base_size += target_size[0];
	}
#endif
	else {
		target_offset[0] = target_base_size;
		target_size[0] = 
			xp_sizeof(struct ipt_entry_target) +
			IPT_ALIGN(xp_sizeof(int));
		target_base_size += target_size[0];
	}

	size = xp_sizeof(struct ipt_entry) + match_base_size + target_base_size;

	#define XXXX 10240
	{
		xp_size_t cap = (size / XXXX + 1) * XXXX;
		if (cap > iptc->entry_capacity) {
			if (iptc->entry != XP_NULL) xp_free (iptc->entry);
			iptc->entry = (struct ipt_entry*)xp_malloc (cap);
			if (iptc->entry == XP_NULL) iptc->entry_capacity = 0;
			else iptc->entry_capacity = cap;
		}
		entry = iptc->entry;
	}
	/* entry = (struct ipt_entry*) xp_malloc (size); */
	if (entry == XP_NULL) return XP_NULL;

	/* build main entry */
	xp_memset (entry, 0, size);
	match_base = (struct ipt_entry_match*)
		((xp_byte_t*)entry + xp_sizeof(struct ipt_entry));
	target_base = (struct ipt_entry_target*)
		((xp_byte_t*)entry + xp_sizeof(struct ipt_entry) + match_base_size);

	entry->target_offset = xp_sizeof(struct ipt_entry) + match_base_size;
	entry->next_offset = size;

	entry->ip.proto = proto;
	entry->ip.src.s_addr = src_ip;
	entry->ip.dst.s_addr = dst_ip;
	entry->ip.smsk.s_addr = src_mask;
	entry->ip.dmsk.s_addr = dst_mask; 
	if (inifce != XP_NULL) {
		n = xp_mstrxcpy (entry->ip.iniface,
			xp_countof(entry->ip.iniface), dev_in);
		/* include the null-terminator in match */
		if (n > 0) xp_memset (entry->ip.iniface_mask, 0xFF, n + 1);
	}
	if (outifce != XP_NULL) {
		n = xp_mstrxcpy (entry->ip.outiface,
			xp_countof(entry->ip.outiface), dev_out);
		/* include the null-terminator in match */
		if (n > 0) xp_memset (entry->ip.outiface_mask, 0xFF, n + 1);
	}

	if (inv_proto) entry->ip.invflags |= IPT_INV_PROTO;
	if (inv_src_ip) entry->ip.invflags |= IPT_INV_SRCIP;
	if (inv_dst_ip) entry->ip.invflags |= IPT_INV_DSTIP;
	if (inv_inifce) entry->ip.invflags |= IPT_INV_VIA_IN;
	if (inv_outifce) entry->ip.invflags |= IPT_INV_VIA_OUT;

	/* build matches */
	if (proto == IPPROTO_TCP &&
	    (!(src_pt_start == 0x0000 && src_pt_end == 0xFFFF) ||
	     !(dst_pt_start == 0x0000 && dst_pt_end == 0xFFFF))) {
		struct ipt_tcp* data;
		match = (struct ipt_entry_match*)
			((xp_byte_t*)match_base + match_offset[0]);
		data = (struct ipt_tcp*)match->data;
	
		match->u.match_size = match_size[0];
		xp_mstrxcpy (match->u.user.name, 
			xp_countof(match->u.user.name), "tcp");
		data->spts[0] = src_pt_start;
		data->spts[1] = src_pt_end;
		data->dpts[0] = dst_pt_start;
		data->dpts[1] = dst_pt_end;

		if (inv_src_pt) data->invflags |= IPT_TCP_INV_SRCPT;
		if (inv_dst_pt) data->invflags |= IPT_TCP_INV_DSTPT;
	}

	if (proto == IPPROTO_UDP &&
	    (!(src_pt_start == 0x0000 && src_pt_end == 0xFFFF) ||
	     !(dst_pt_start == 0x0000 && dst_pt_end == 0xFFFF))) {
		struct ipt_udp* data;
		match = (struct ipt_entry_match*)
			((xp_byte_t*)match_base + match_offset[1]);
		data = (struct ipt_udp*)match->data;
	
		match->u.match_size = match_size[1];
		xp_mstrxcpy (match->u.user.name, 
			xp_countof(match->u.user.name), "udp");
		data->spts[0] = src_pt_start;
		data->spts[1] = src_pt_end;
		data->dpts[0] = dst_pt_start;
		data->dpts[1] = dst_pt_end;

		if (inv_src_pt) data->invflags |= IPT_TCP_INV_SRCPT;
		if (inv_dst_pt) data->invflags |= IPT_TCP_INV_DSTPT;
	}

	if (match_mac != 0) {
		struct ipt_mac_info* data;
		match = (struct ipt_entry_match*)
			((xp_byte_t*)match_base + match_offset[2]);
		data = (struct ipt_mac_info*)match->data;

		match->u.match_size = match_size[2];
		xp_mstrxcpy (match->u.user.name, 
			xp_countof(match->u.user.name), "mac");
		xp_memcpy (data->srcaddr, src_mac, xp_sizeof(data->srcaddr));
		data->invert = (match_mac > 0)? 0: 1;
	}
	if (match_rate != 0) {
		struct ipt_rateinfo* data;
		match = (struct ipt_entry_match*)
			((xp_byte_t*)match_base + match_offset[3]);
		data = (struct ipt_rateinfo*)match->data;

		/* 
		 * 1/10,000 sec period => max of 10,000/sec.  
		 * Min rate is then 429490 seconds, or one every 59 hours. 
		 */

		match->u.match_size = match_size[3];
		xp_mstrxcpy (match->u.user.name, 
			xp_countof(match->u.user.name), "limit");

		/* Average secs between packets * scale */
		data->avg = rate_avg;
		/* Period multiplier for upper limit. */
		data->burst = rate_burst;
	}
	if (match_mark != 0) {
		struct ipt_mark_info* data;
		match = (struct ipt_entry_match*)
			((xp_byte_t*)match_base + match_offset[4]);
		data = (struct ipt_mark_info*)match->data;

		match->u.match_size = match_size[4];
		xp_mstrxcpy (match->u.user.name, 
			xp_countof(match->u.user.name), "mark");

		data->mark = mark;
		data->mask = mark_mask;
		data->invert = (match_mark > 0)? 0: 1;
	}

	/* build target */
	target = (struct ipt_entry_target*)
		((xp_byte_t*)target_base + target_offset[0]);
	target->u.target_size = target_size[0];
#ifdef XP_CHAR_IS_MCHAR
	xp_strxcpy (target->u.user.name, 
		xp_countof(target->u.user.name), target_name);
#else
	xp_wcstomcs (target_name, 
		target->u.user.name, xp_countof(target->u.user.name));
#endif
	if (target_type == TARGET_REDIRECT) {
		struct ip_nat_multi_range* data;
		data = (struct ip_nat_multi_range*)target->data;

		data->rangesize = 1;
		data->range[0].flags = IP_NAT_RANGE_PROTO_SPECIFIED;

		// TODO: more protocol check...
		if (proto == IPPROTO_TCP) {
			data->range[0].min.tcp.port = xp_hton16(redir_pt_start);
			data->range[0].max.tcp.port = xp_hton16(redir_pt_end);
		}
		else if (proto == IPPROTO_UDP) {
			data->range[0].min.udp.port = xp_hton16(redir_pt_start);
			data->range[0].max.udp.port = xp_hton16(redir_pt_end);
		}
	}	
	else if (target_type == TARGET_MARK) {
		struct ipt_mark_target_info* data;
		data = (struct ipt_mark_target_info*)target->data;
		data->mark = target_mark;
	}
	else if (target_type == TARGET_REJECT) {
		struct ipt_reject_info* data;
		data = (struct ipt_reject_info*)target->data;
		data->with = reject_type;
	}
	else if (target_type == TARGET_SNAT) {
		struct ip_nat_multi_range* data;
		data = (struct ip_nat_multi_range*)target->data;

		data->rangesize = 1;
		data->range[0].flags = IP_NAT_RANGE_MAP_IPS;
		data->range[0].min_ip = snat_ip_start;
		data->range[0].max_ip = snat_ip_end;
	}
	else if (target_type == TARGET_DNAT) {
		struct ip_nat_multi_range* data;
		data = (struct ip_nat_multi_range*)target->data;

		data->rangesize = 1;
		data->range[0].flags = IP_NAT_RANGE_MAP_IPS;
		data->range[0].min_ip = snat_ip_start;
		data->range[0].max_ip = snat_ip_end;

		if (redir_pt_start > 0 && redir_pt_end > 0) {
			if (proto == IPPROTO_TCP) {
				data->range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
				data->range[0].min.tcp.port = xp_hton16(redir_pt_start);
				data->range[0].max.tcp.port = xp_hton16(redir_pt_end);
			}
			else if (proto == IPPROTO_UDP) {
				data->range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
				data->range[0].min.udp.port = xp_hton16(redir_pt_start);
				data->range[0].max.udp.port = xp_hton16(redir_pt_end);
			}
		}
	}
#if 0
	else if (target_type == TARGET_MASQUERADE) {
		struct ip_nat_multi_range* data;
		data = (struct ip_nat_multi_range*)target->data;
		/* TODO */
	}
#endif

	return entry;
}

#endif
